package org.jing.ext.security;

import org.jing.core.lang.JingException;

import javax.crypto.Cipher;
import java.security.Key;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * Description: <br>
 *
 * @author bks <br>
 * @since 2021-03-03 <br>
 */
public class RSABase64 {
    public static String encryptByPublicKey(String str, String charSet, String publicKey) {
        try {
            byte[] decoded = Common.getBase64Decoder().decode(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, pubKey);
            return Common.getBase64Encoder().encodeToString(cipher.doFinal(str.getBytes(charSet))).replaceAll("\\s", "");
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "Failed to encrypt.");
        }
    }

    public static String decryptByPrivateKey(String str, String charSet, String privateKey) {
        try {
            byte[] inputByte = Common.getBase64Decoder().decode(str.getBytes(charSet));
            byte[] decoded = Common.getBase64Decoder().decode(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, priKey);
            return new String(cipher.doFinal(inputByte), charSet);
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "Failed to decrypt.");
        }
    }

    public static byte[] decryptByPrivateKey(byte[] buffer, PrivateKey privateKey) {
        return dealByKey(buffer, privateKey, Cipher.DECRYPT_MODE);
    }

    public static String encryptByPrivateKey(String str, String charSet, String privateKey) {
        try {
            //base64编码的公钥
            byte[] decoded = Common.getBase64Decoder().decode(privateKey);
            RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
            //RSA加密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.ENCRYPT_MODE, priKey);
            return Common.getBase64Encoder().encodeToString(cipher.doFinal(str.getBytes(charSet))).replaceAll("\\s", "");
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "Failed to encrypt.");
        }
    }

    public static String decryptByPublicKey(String str, String charSet, String publicKey) {
        try {
            byte[] inputByte = Common.getBase64Decoder().decode(str.getBytes(charSet));
            byte[] decoded = Common.getBase64Decoder().decode(publicKey);
            RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
            //RSA解密
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, pubKey);
            return new String(cipher.doFinal(inputByte), charSet);
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t, "Failed to decrypt.");
        }
    }

    public static byte[] decryptByPublicKey(byte[] buffer, PublicKey publicKey) {
        return dealByKey(buffer, publicKey, Cipher.DECRYPT_MODE);
    }

    public static byte[] dealByKey(byte[] buffer, Key key, int model) {
        try {
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(model, key, new SecureRandom());
            return cipher.doFinal(buffer);
        }
        catch (JingException e) {
            throw e;
        }
        catch (Throwable t) {
            throw new JingException(t);
        }
    }
}
